---
title: "JSX"
description: "JSX syntax in ReScript and React"
canonical: "/docs/manual/jsx"
section: "Language Features"
order: 18
---

# JSX

Would you like some HTML syntax in your ReScript? If not, quickly skip over this section and pretend you didn't see anything!

ReScript supports the JSX syntax, with some slight differences compared to the one in [ReactJS](https://facebook.github.io/react/docs/introducing-jsx.html). ReScript JSX isn't tied to ReactJS; they translate to normal function calls:

**Note** for [ReScriptReact](../react/introduction.mdx) readers: this isn't what ReScriptReact turns JSX into, in the end. See Usage section for more info.

## Capitalized

<CodeTab labels={["ReScript", "JS Output"]}>

```res
<MyComponent name={"ReScript"} />
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsx(MyComponent, {
  name: "ReScript",
});
```

</CodeTab>

becomes

<CodeTab labels={["ReScript", "JS Output"]}>

```res
React.jsx(MyComponent.make, {name: {"ReScript"}})
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsx(MyComponent, {
  name: "ReScript",
});
```

</CodeTab>

## Uncapitalized

<CodeTab labels={["ReScript", "JS Output"]}>

```res
<div onClick={handler}> child1 child2 </div>
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsxs("div", {
  children: [child1, child2],
  onClick: handler,
});
```

</CodeTab>

becomes

<CodeTab labels={["ReScript", "JS Output"]}>

```res
ReactDOM.jsxs("div", {onClick: {handler}, children: React.array([child1, child2])})
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsxs("div", {
  children: [child1, child2],
  onClick: handler,
});
```

</CodeTab>

## Fragment

<CodeTab labels={["ReScript", "JS Output"]}>

```res
<> child1 child2 </>
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsxs(JsxRuntime.Fragment, {
  children: [child1, child2],
});
```

</CodeTab>

becomes

<CodeTab labels={["ReScript", "JS Output"]}>

```res
React.jsxs(React.jsxFragment, {children: React.array([child1, child2])})
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsxs(JsxRuntime.Fragment, {
  children: [child1, child2],
});
```

</CodeTab>

### Children

<CodeTab labels={["ReScript", "JS Output"]}>

```res
<MyComponent> child1 child2 </MyComponent>
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsxs(MyComponent, {
  children: [child1, child2],
});
```

</CodeTab>

This is the syntax for passing a list of two items, `child1` and `child2`, to the children position. It transforms to a list containing `child1` and `child2`:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
React.jsxs(MyComponent.make, {children: React.array([child1, child2])})
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsxs(MyComponent, {
  children: [child1, child2],
});
```

</CodeTab>

**Note** again that this isn't the transform for ReScriptReact; ReScriptReact turns the final list into an array. But the idea still applies.

So naturally, `<MyComponent> myChild </MyComponent>` is transformed to `React.jsx(MyComponent.make, {children: myChild})`. I.e. whatever you do, the arguments passed to the children position will be wrapped in a list.

## Usage

See [ReScriptReact Elements & JSX](../react/elements-and-jsx.mdx) for an example application of JSX, which transforms the above calls into a ReScriptReact-specific call.

Here's a JSX tag that shows most of the features.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
<MyComponent
  booleanAttribute={true}
  stringAttribute="string"
  intAttribute=1
  forcedOptional=?{Some("hello")}
  onClick={handleClick}>
  <div> {React.string("hello")} </div>
</MyComponent>
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsx(Playground$MyComponent, {
  children: JsxRuntime.jsx("div", {
    children: "hello",
  }),
  booleanAttribute: true,
  stringAttribute: "string",
  intAttribute: 1,
  forcedOptional: "hello",
  onClick: handleClick,
});
```

</CodeTab>

## Departures From JS JSX

- Attributes and children don't mandate `{}`, but we show them anyway for ease of learning. Once you format your file, some of them go away and some turn into parentheses.
- Props spread is supported, but there are some restrictions (see below).
- Punning!
- Props and tag names have to follow ReScript's restrictions on identifiers at the exception of hyphens for lowercase tags ([see below](#hyphens-in-tag-names)).

### Spread Props

**Since 10.1**

JSX props spread is supported now, but in a stricter way than in JS.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
<Comp {...props} a="a" />
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsx(Comp.make, {
  a: "a",
  b: "b",
});
```

</CodeTab>

Multiple spreads are not allowed:

<CodeTab labels={["ReScript"]}>

```res
<NotAllowed {...props1} {...props2} />
```

</CodeTab>

The spread must be at the first position, followed by other props:

<CodeTab labels={["ReScript"]}>

```res
<NotAllowed a="a" {...props} />
```

</CodeTab>

### Punning

"Punning" refers to the syntax shorthand for when a label and a value are the same. For example, in JavaScript, instead of doing `return {name: name}`, you can do `return {name}`.

JSX supports punning. `<input checked />` is just a shorthand for `<input checked=checked />`. The formatter will help you format to the punned syntax whenever possible. This is convenient in the cases where there are lots of props to pass down:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
<MyComponent isLoading text onClick />
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsx(MyComponent, {
  text: text,
  isLoading: true,
  onClick: onClick,
});
```

</CodeTab>

Consequently, a JSX component can cram in a few more props before reaching for extra libraries solutions that avoids props passing.

**Note** that this is a departure from ReactJS JSX, which does **not** have punning. ReactJS' `<input checked />` desugars to `<input checked=true />`, in order to conform to DOM's idioms and for backward compatibility.

### Hyphens in tag names

**Since 11.1**

JSX now supports lowercase tags with hyphens in their name. This allows to bind
to web components.

Note though that props names can't have hyphens, you should use `@as` to bind to
such props in your custom `JsxDOM.domProps` type ([see generic JSX transform](#generic-jsx-transform-jsx-beyond-react-experimental)).

<CodeTab labels={["ReScript", "JS Output"]}>

```res
<model-viewer src touchActions="pan-y"></model-viewer>
```

```js
import * as JsxRuntime from "react/jsx-runtime";

JsxRuntime.jsx("model-viewer", {
  "touch-actions": "pan-y",
  src: src,
});
```

</CodeTab>

## Generic JSX transform: JSX beyond React (experimental)

**Since 11.1**

While ReScript comes with first class support for JSX in React, it's also possible to have ReScript delegate JSX to other frameworks. You do that by configuring a _generic JSX transform_.

This is what you need to do to use a generic JSX transform:

1. Make sure you have a ReScript module that [implements the functions and types necessary for the JSX transform](#implementing-a-generic-jsx-transform-module).
2. Configure `rescript.json` to delegated JSX to that module.

That's it really. We'll expand on each point below.

### Configuration

You configure a generic JSX transform by putting any module name in the `module` config of JSX in `rescript.json`. This can be _any valid module name_. Example part from `rescript.json`:

```json
"jsx": {
  "module": "Preact"
 },
```

This will now put the `Preact` module in control of the generated JSX calls. The `Preact` module can be defined by anyone - locally in your project, or by a package. As long a it's available in the global scope. The JSX transform will delegate any JSX related code to `Preact`.

#### What about `@react.component` for components?

`@react.component` will still be available, and so is a generic `@jsx.component` notation. Both work the same way.

### Usage Example

Here's a quick usage example (the actual definition of `Preact.res` comes below):

First, configure `rescript.json`:

```json
"jsx": {
  "module": "Preact"
 },
```

Now you can build Preact components:

```rescript
// Name.res
@jsx.component // or @react.component if you want
let make = (~name) => Preact.string(`Hello ${name}!`)
```

And you can use them just like normal with JSX:

```rescript
let name = <Name name="Test" />
```

#### File level configuration

You can configure what JSX transform is used at the file level via `@@jsxConfig`, just like before. Like:

```rescript
@@jsxConfig({module_: "Preact"})
```

This can be convenient if you're mixing different JSX frameworks in the same project.

### Implementing a generic JSX transform module

Below is a full list of everything you need in a generic JSX transform module, including code comments to clarify. It's an example implementation of a `Preact` transform, so when doing this for other frameworks you'd of course adapt what you import from, and so on.

> You can easily copy-paste-and-adapt this to your needs if you're creating bindings to a JSX framework. Most often, all you'll need to change is what the `@module("") external` points to, so the runtime calls point to the correct JS module.

<CodeTab labels={["ReScript"]}>

```rescript
// Preact.res
/* Below is a number of aliases to the common `Jsx` module */
type element = Jsx.element

type component<'props> = Jsx.component<'props>

type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return>

@module("preact/jsx-runtime")
external jsx: (component<'props>, 'props) => element = "jsx"

@module("preact/jsx-runtime")
external jsxKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsx"

@module("preact/jsx-runtime")
external jsxs: (component<'props>, 'props) => element = "jsxs"

@module("preact/jsx-runtime")
external jsxsKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsxs"

/* These identity functions and static values below are optional, but lets
you move things easily to the `element` type. The only required thing to
define though is `array`, which the JSX transform will output. */
external array: array<element> => element = "%identity"
@val external null: element = "null"

external float: float => element = "%identity"
external int: int => element = "%identity"
external string: string => element = "%identity"
external promise: promise<element> => element = "%identity"

/* These are needed for Fragment (<> </>) support */
type fragmentProps = {children?: element}

@module("preact/jsx-runtime") external jsxFragment: component<fragmentProps> = "Fragment"

/* The Elements module is the equivalent to the ReactDOM module in Preact. This holds things relevant to _lowercase_ JSX elements. */
module Elements = {
  /* Here you can control what props lowercase JSX elements should have.
  A base that the React JSX transform uses is provided via JsxDOM.domProps,
  but you can make this anything. The editor tooling will support
  autocompletion etc for your specific type. */
  type props = JsxDOM.domProps

  @module("preact/jsx-runtime")
  external jsx: (string, props) => Jsx.element = "jsx"

  @module("preact/jsx-runtime")
  external div: (string, props) => Jsx.element = "jsx"

  @module("preact/jsx-runtime")
  external jsxKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "jsx"

  @module("preact/jsx-runtime")
  external jsxs: (string, props) => Jsx.element = "jsxs"

  @module("preact/jsx-runtime")
  external jsxsKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "jsxs"

  external someElement: element => option<element> = "%identity"
}
```

</CodeTab>

As you can see, most of the things you'll want to implement will be copy paste from the above. But do note that **everything needs to be there unless explicitly noted** or the transform will fail at compile time.

To enable this, you need to configure the `jsx` `module` in your `rescript.json`:

```json
{
  "jsx": {
    "version": 4,
    "module": "Preact"
  }
}
```

_value "Preact" is the name of the module that implements the generic JSX transform._

## Preserve mode

**Since 12.0**

JSX Preserve Mode keeps JSX syntax in the compiled JavaScript output instead of transforming it to `JsxRuntime.jsx` calls. This lets bundlers (ESBuild, SWC, Next.js) or React Server Components handle JSX transformation.

### Configuration

```json
{
  "jsx": {
    "version": 4,
    "preserve": true
  }
}
```

<CodeTab labels={["ReScript", "JS Output"]}>

```res
let c1 = <div className="foo"> {React.string("Hello")} </div>
let c2 = <MyComponent text="hey" isLoading=true onClick />
```

```js
let c1 = <div className={"foo"}>{"Hello"}</div>;

let c2 = <MyComponent.make text={"hey"} isLoading={true} onClick={onClick} />;
```

</CodeTab>

Note that the JSX output is functional but not always the most aesthetically pleasing.
